<?php
/**
 * @author: Gardennet
 * @date: 16-12-15
 * @email: lwp8800@sina.com
 */
namespace App\Services\Wechat;

use DOMDocument;
use Doctrine\Common\Cache\FilesystemCache;
use App\Services\Wechat\sdk\components\messageCrypt\WxBizMsgCrypt;

Trait WechatOpenTrait
{
    /**
     * 开放平台访问入口
     * 主函数
     * @return string|void
     */
    public function main()
    {
        $param = $this->getParam();
        $encryptMsg = file_get_contents('php://input');

        app('record')->log('开放平台返回数据:'.json_encode($encryptMsg, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
        app('record')->log('开放平台返回参数:'.json_encode($param, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));

        $this->protect($encryptMsg);
        if($param['action']=='get_ticket')//获取平台验证码
        {
            $result = $this->getVerifyTicket($param,$encryptMsg);
            app('record')->log('获取平台验证码:'.json_encode($result, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
            if($result){
                echo "success";exit;
            }
        }else{//平台消息接入测试
            $data = $this->request($param,$encryptMsg);
            list($content, $type) = $this->reply($data);
            $this->response($data,$param,$content, $type);
        }

    }

    /**
     * 防护函数
     * @param $encryptMsg
     * @return bool
     */
    public function protect($encryptMsg)
    {
        if(empty($encryptMsg))
        {
            echo '非法访问！';exit;
        }
    }


    /**
     * 获取参数
     * @return array|mixed
     */
    public function getParam()
    {
        $param = $this->request->query->getIterator();
        $param['timestamp'] = empty($param['timestamp']) ? "" : trim($param['timestamp']);
        $param['nonce'] = empty($param['nonce']) ? "" : trim($param['nonce']);
        $param['encrypt_type'] = empty($param['encrypt_type']) ? "" : trim($param['encrypt_type']);
        $param['msg_signature'] = empty($param['msg_signature']) ? "" : trim($param['msg_signature']);
        $param['signature'] = empty($param['signature']) ? "" : trim($param['signature']);
        $param['action'] = empty($param['action']) ? "get_ticket" : trim($param['action']);
        return $param;
    }

    /**
     * 获取 component_verify_ticket
     * @param $timeStamp
     * @param $nonce
     * @param $encrypt_type
     * @param $msg_sign
     * @param $encryptMsg
     * @return bool
     */
    public function getVerifyTicket($param,$encryptMsg)
    {
        $pc = new WxBizMsgCrypt($this->getOpenInstance()->componentToken, $this->getOpenInstance()->componentEncodingAesKey, $this->getOpenInstance()->componentAppid);
        $xml_tree = new DOMDocument();
        $xml_tree->loadXML($encryptMsg);
        $array_e = $xml_tree->getElementsByTagName('Encrypt');
        $encrypt = $array_e->item(0)->nodeValue;
        $format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%s]]></Encrypt></xml>";
        $from_xml = sprintf($format, $encrypt);
        $msg = '';
        app('record')->log('获取 component_verify_ticket 1:'.json_encode($pc, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
        $errCode = $pc->decryptMsg($param['msg_signature'], $param['timestamp'], $param['nonce'], $from_xml, $msg);
        app('record')->log('获取 component_verify_ticket 2:'.json_encode($errCode, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
        if ($errCode == 0) {
            $xml = new DOMDocument();
            $xml->loadXML($msg);
            $array_e = $xml->getElementsByTagName('ComponentVerifyTicket');
            $component_verify_ticket = $array_e->item(0)->nodeValue;
            $cacheDir = dirname(__DIR__).'/Wechat/sdk/components/cache';
            $cache   = new FilesystemCache($cacheDir);
            $cache->save('cache_wechat_sdk_mp_component_verify_ticket', $component_verify_ticket, '580');
            app('record')->log('获取 component_verify_ticket 3:'.json_encode($component_verify_ticket, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
            return true;
        }else{
            return false;
        }
    }


    /**
     * 微信接入测试请求
     * @param $param
     * @param $encryptMsg
     * @return array|int
     */
    public function request($param,$encryptMsg)
    {
        $pc = new WxBizMsgCrypt($this->getOpenInstance()->componentToken, $this->getOpenInstance()->componentEncodingAesKey, $this->getOpenInstance()->componentAppid);
        $sMsg = '';
        $errCode = $pc->decryptMsg($param['msg_signature'], $param['timestamp'], $param['nonce'], $encryptMsg, $sMsg);
        if ($errCode == 0) {
            $data = array();
            $xml = new \SimpleXMLElement($sMsg);
            $xml || exit();

            foreach ($xml as $key => $value) {
                $data[$key] = strval($value);
            }
            app('record')->log('微信接入测试请求:'.json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
            return $data;
        }
        else {
            return $errCode;
        }
    }


    /**
     * 微信接入测试回应
     * @param $data
     * @param $param
     * @param $content
     * @param string $type
     * @param int $flag
     */
    public function response($data,$param,$content, $type = 'text', $flag = 0)
    {
        if($content=='')//直接回复空字符串
        {
            echo '';exit;
        }
        $data = array('ToUserName' => $data['FromUserName'], 'FromUserName' => $data['ToUserName'], 'CreateTime' => time(), 'MsgType' => $type);
        $data['Content'] = $content;
        $data['FuncFlag'] = $flag;
        $xml = new \SimpleXMLElement('<xml></xml>');
        $this->data2xml($xml, $data);
        app('record')->log('微信接入测试回应 1:'.json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
        if (isset($param['encrypt_type']) && ($param['encrypt_type'] == 'aes'))
        {
            $pc = new WxBizMsgCrypt($this->getOpenInstance()->componentToken, $this->getOpenInstance()->componentEncodingAesKey, $this->getOpenInstance()->componentAppid);
            app('record')->log('微信接入测试回应 2:'.json_encode($pc, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
            $sRespData = str_replace('<?xml version="1.0"?>', '', $xml->asXML());
            $encryptMsg = '';
            $errCode = $pc->encryptMsg($sRespData, $param['timestamp'], $param['nonce'], $encryptMsg);
            app('record')->log('微信接入测试回应 3:'.json_encode($encryptMsg, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
            if ($errCode == 0) {
                echo $encryptMsg;exit;
            }
            else {
                echo $errCode;exit;
            }
        }
        else {
            echo $xml->asXML();exit;
        }
    }


    /**
     * 微信接入测试回复
     * @param $data
     * @return array
     */
    public function reply($data)
    {
        app('record')->log('微信接入测试回复 1:'.json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
        $keyword = isset($data['Content']) ? $data['Content'] : (isset($data['EventKey']) ? $data['EventKey'] : '');
        if($data['ToUserName'] == 'gh_3c884a361561'){//微信官方专用测试号
            if ($data['MsgType'] == 'event') {
                return array($data['Event'] . 'from_callback', 'text');
            }

            if ($keyword == 'TESTCOMPONENT_MSG_TYPE_TEXT') {
                return array('TESTCOMPONENT_MSG_TYPE_TEXT_callback', 'text');
            }
            if (strstr($keyword, 'QUERY_AUTH_CODE:')) {
                $t = explode(':', $keyword);
                $query_auth_code = $t[1];
                app('record')->log('微信接入测试回应 2:'.json_encode($query_auth_code, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
                $authInfo = $this->getOpenInstance()->getComponentAuthInfo($query_auth_code);
                $access_token = $authInfo['authorization_info']['authorizer_access_token'];
                app('record')->log('微信接入测试回应 3:'.json_encode($access_token, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
                $sendData = [
                    'touser' => $data['FromUserName'],
                    'msgtype' => 'text',
                    'text' => [
                        'content' => $query_auth_code.'_from_api'
                    ],
                ];
                $this->getOpenInstance()->sendMessage($sendData,$access_token);

            }
        }else{
            return ['', 'text'];
        }
    }


    /**
     * @param $xml
     * @param $data
     * @param string $item
     */
    private function data2xml($xml, $data, $item = 'item')
    {
        foreach ($data as $key => $value) {
            is_numeric($key) && ($key = $item);
            if (is_array($value) || is_object($value)) {
                $child = $xml->addChild($key);
                $this->data2xml($child, $value, $item);
            }
            else if (is_numeric($value)) {
                $child = $xml->addChild($key, $value);
            }
            else {
                $child = $xml->addChild($key);
                $node = dom_import_simplexml($child);
                $node->appendChild($node->ownerDocument->createCDATASection($value));
            }
        }
    }
}